#include <cnix/errno.h>
#include <cnix/string.h>
#include <cnix/fs.h>
#include <cnix/kernel.h>
#include <cnix/driver.h>

extern int ide_open(dev_t major, dev_t minor, int flags);
extern size_t ide_size(dev_t major, dev_t minor);

int cdrom_open(dev_t dev, int flags)
{
	if(ide_open(CDROM_MJ_DEV, dev, flags) < 0)
		return -ENXIO;

	return 0;
}

int cdrom_close(dev_t dev, int flags)
{
	return 0;
}

ssize_t cdrom_read(
	dev_t dev,
	char * buffer,
	size_t count,
	off_t off,
	int * error,
	int openflags
	)
{
	int ret;
	unsigned long block;
	off_t pos, inblock;
	int readed, reading;
	struct buf_head * bh;
	size_t size = ide_size(CDROM_MJ_DEV, dev);

	readed = 0;
	pos = off;

	inblock = pos % CDROM_BLOCKSIZ;
	if(inblock){
		reading = CDROM_BLOCKSIZ - inblock;
		if(reading > count)
			reading = count;

		if(reading > size - pos)
			reading = size - pos;

		if(reading <= 0)
			return readed;

		block = pos / CDROM_BLOCKSIZ;
		bh = bread(DEVNUM(CDROM_MJ_DEV, dev), block);
		if(!bh){
			*error = -EIO;
			return 0;
		}

		ret = memcpy_to_user(
			&buffer[readed], &bh->b_data[inblock], reading
			);
		if(ret < 0){
			brelse(bh);
			*error = -EFAULT;
			return 0;
		}

		brelse(bh);

		readed += reading;
		pos += reading;
	}

	while(count > readed){
		if((count - readed) > CDROM_BLOCKSIZ)
			reading = CDROM_BLOCKSIZ;
		else
			reading = count - readed;

		if(reading > size - pos)
			reading = size -  pos;

		if(reading <= 0)
			break;

		block = pos / CDROM_BLOCKSIZ;

		bh = bread(DEVNUM(CDROM_MJ_DEV, dev), block);
		if(!bh){
			*error = -EIO;
			return 0;
		}

		ret = memcpy_to_user(&buffer[readed], bh->b_data, reading);
		if(ret < 0){
			brelse(bh);
			*error = -EFAULT;
			return 0;
		}

		brelse(bh);

		readed += reading;
		pos += reading;
	}

	return readed;
}

ssize_t cdrom_write(
	dev_t dev,
	const char * buffer,
	size_t count,
	off_t off,
	int * error,
	int openflags
	)
{
	return -ENOTSUP;
}

/*
 * caller has locked inode
 */
int cdrom_ioctl(dev_t dev, int request, void * data, int openflags)
{
	return -ENOTSUP;
}

void cdrom_driver_init(void)
{
	printk("cdrom driver initializing\n");
}
